package com.niodata.dp.plugin.spring;

import com.niodata.dp.mybatis.DpMybatisTypeHandler;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.github.lukehutch.fastclasspathscanner.matchprocessor.ImplementingClassMatchProcessor;
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;

@Configuration
public class SpringMyBatisConfig {

  private static Logger logger = LoggerFactory.getLogger(SpringMyBatisConfig.class);

  @Autowired
  private ApplicationContext applicationContext;

  @Bean
  public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
    SqlSessionFactoryBean factoryBean =
      new SqlSessionFactoryBean() {
        protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
          try {
            SqlSessionFactory factory = super.buildSqlSessionFactory();
            registerTypeHandler(factory);
            return factory;
          } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            return null;
          }
        }
      };
    factoryBean.setDataSource(applicationContext.getBean(DataSource.class));
    ResourceLoader loader = new DefaultResourceLoader();
    String resource = "classpath:mybatis-config.xml";
    factoryBean.setConfigLocation(loader.getResource(resource));
    //factoryBean.setMapperLocations(this.getMapperResources());
    factoryBean.setSqlSessionFactoryBuilder(new SqlSessionFactoryBuilder());
    return factoryBean;
  }


  private void registerTypeHandler(SqlSessionFactory factory) throws Exception {
    TypeHandlerRegistry registry = factory.getConfiguration().getTypeHandlerRegistry();
    String pkgs = "com.niodata.dp.mybatis";
    String[] allPkgs = pkgs.split(";");
    for (String pkg : allPkgs) {
      pkg = pkg.trim();
      if (pkg.length() == 0) {
        continue;
      }
      FastClasspathScanner scanner = new FastClasspathScanner(pkg);
      scanner.matchClassesImplementing(
        DpMybatisTypeHandler.class,
        new ImplementingClassMatchProcessor() {
          @Override
          public void processMatch(Class implementingClass) {
          }
        });
      ScanResult result = scanner.scan();
      List<String> classes = result.getNamesOfClassesImplementing(DpMybatisTypeHandler.class);
      for (String clazz : classes) {
        Class classEntity = Class.forName(clazz);
        if (Modifier.isAbstract(classEntity.getModifiers())) {
          continue;
        }
        DpMybatisTypeHandler handler =
          (DpMybatisTypeHandler) (Class.forName(clazz).newInstance());
        registry.register(
          handler.getJavaClass(), handler.getJdbcType(), handler.getTypeHandlerClass());
      }
    }
  }

}
